<?php
/*
*All rights reserved: Json.
*Links:http://www.liyaoquan.cn.
*Links:http://imarkchina.cn.
*Links:http://www.imarkchina.cn.
*/

Class DbPDO extends Db {
	Private Static $_instance = null;

	Public Static function getInstance() {
		if (is_null(self::$_instance)) self::$_instance = new self();
		return self::$_instance;
	}

	Private Static $_connect = array();

	Private function _connect() {
		$connectId = md5(serialize($this->config));
		if (isset(self::$_connect[$connectId])) return self::$_connect[$connectId];
		if (!$this->config['DB_DNS']) {
			if (!$this->config['DB_TYPE']) {
				throw new Exception(__METHOD__.' [未定义数据库类型]');
			}
			$dsn = $this->config['DB_TYPE'].':';
			$dsn .= 'host='.$this->config['DB_HOST'];
			$dsn .= ';dbname='.$this->config['DB_NAME'];
			$dsn .= ';port='.$this->config['DB_PORT'];
		} else {
			$dsn = $this->config['DB_DSN'];
		}

		if ($this->config['DB_TYPE'] == 'sqlite') { //避免自动创建sqlite文件
			$file = ltrim($this->config['DB_DSN'], 'sqlite:');
			if (!is_file($file))
			throw new Exception(__METHOD__.' [sqlite数据库不存在: '.$file.']');
		}
		try{
			self::$_connect[$connectId] = new PDO($dsn,
			$this->config['DB_USER'],
			$this->config['DB_PASSWORD'],
			array(PDO::ATTR_PERSISTENT => $this->config['DB_LONG_CONNECT']));
			self::$_connect[$connectId]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //让PDO抛出异常
			if ($this->config['DB_TYPE'] == 'mysql') {
				self::$_connect[$connectId]->query('SET NAMES '.$this->config['DB_CHARSET']);
			}
			return self::$_connect[$connectId];
		} catch (Exception $e) {
			throw new Exception(__METHOD__.' [数据库连接失败: '.iconv('GBK', 'UTF-8', $e->getMessage()).']');
		}
	}

	Public function escapeString($string) {
		$PDO = $this->_connect();
		return substr($PDO->quote($string), 1, -1);
	}

	Public function query($sql, $method = '') {
		$startTime = microtime(true);
		$PDO = $this->_connect();
		$method = strtolower($method);
		try{
			$sql = $this->parse('prefix', $sql);
			$stmt = $PDO->prepare($sql);
			$result = $stmt->execute();
			if (!$method) return $result;
			$stmt->setFetchMode(PDO::FETCH_ASSOC);
			$result = array();
			switch ($method) {
				case 'fetch':
				$result = $stmt->fetch();
				break;
				case 'fetchall':
				$result = $stmt->fetchAll();
				break;
				case 'count':
				$result = $stmt->fetchColumn();
				break;
				case 'insert':
				$result = $PDO->lastInsertId();
				break;
				case 'update':
				case 'delete':
				$result = $stmt->rowCount();
				break;
			}
			self::$countQuery++;
			$stopTime = microtime(true);
			J::debug('[用时<font color="red">'.round(($stopTime - $startTime), 4).'</font>秒]: '.$sql);
			return $result === false ? array() : $result;
		} catch (Exception $e) {
			throw new Exception(__METHOD__. '[SQL错误: '.$sql.']<br />错误提示: '.$e->getMessage());
		}
	}

	Public function getFields() {
		if (!$this->table) return;
		$dbname = '';
		if (!empty($this->config['DB_NAME']) && !stristr($this->table, $this->config['DB_NAME'])) {
			$dbname = $this->config['DB_NAME'].'.';
		}
		$cacheFile = SQL.$dbname.$this->table.'.php';
		if (is_file($cacheFile)) {
			return unserialize(str_replace('<?php exit();//', '', file_get_contents($cacheFile)));
		}
		$startTime = microtime(true);
		$PDO = $this->_connect();
		try{
			$fields = array();
			switch ($this->config['DB_TYPE']) {
				case 'mysql':
				$sql = 'DESC '.$this->table;
				$stmt = $PDO->prepare($sql);
				$stmt->execute();
				while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
					if ($row['Key'] == 'PRI') {
						$fields['pk'] = strtolower($row['Field']);
					} else {
						$fields[] = strtolower($row['Field']);
					}
				}
				break;
				case 'sqlite':
				$sql = 'PRAGMA table_info('.$this->table.')';
				$stmt = $PDO->prepare($sql);
				$stmt->execute();
				while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
					if (!empty($row['pk'])) {
						$fields['pk'] = strtolower($row['name']);
					} else {
						$fields[] = strtolower($row['name']);
					}
				}
				break;
				default:
				throw new Exception(__METHOD__.' [暂不支持获取'.$this->config['DB_TYPE'].'数据库表结构]');
			}
			//如果不存在主键，则提取第一个字段为主键
			if (!array_key_exists('pk', $fields)) $fields['pk'] = array_shift($fields);
			self::$countQuery++;
			if (!empty($fields['pk'])) {
				$stopTime = microtime(true);
				J::debug('[用时<font color="red">'.round(($stopTime - $startTime), 4).'</font>秒]: '.$sql, 2);
				J::makeDir(dirname($cacheFile));
				file_put_contents($cacheFile, '<?php exit();//'.serialize($fields));
				return $fields;
			}
		} catch (Exception $e) {
			throw new Exception(__METHOD__.' [获取表'.$this->table.'字段失败: '.$e->getMessage().']');
		}
	}

	Public function beginTransaction() {
		$PDO = $this->_connect();
		$PDO->beginTransaction();
	}

	Public function commit() {
		$PDO = $this->_connect();
		$PDO->commit();
	}

	function rollBack() {
		$PDO = $this->_connect();
		$PDO->rollBack();
	}

	Public function lastInsertId() {
		$PDO = $this->_connect();
		return $PDO->lastInsertId();
	}

	Public function getVersion() {
		$PDO = $this->_connect();
		return $PDO->getAttribute(PDO::ATTR_SERVER_VERSION);
	}
}